using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.ServiceProcess;
using System.Text;
using System.Threading;

namespace PaulRz.ServiceUtil
{
	/// <summary>
	/// The most of this code was got from ServiceBase.ServiceInstaller
	/// </summary>
	public sealed class ServiceManager
	{
		private ServiceManager() {}

		public static string GetServiceName()
		{
			string fileName=System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName;
			fileName=new FileInfo(fileName).Name;
			return fileName.Substring(0,fileName.Length-4);

		}

		public static bool ProcessConsoleParameters(string[] args)
		{
			switch(args[0])
			{
				
				case "-i":
					string name=GetServiceName();
					ServiceManager.CreateService(name,name,ServiceStartMode.Automatic,System.Diagnostics.Process.GetCurrentProcess().MainModule.FileName
						,"MG",null );
					return true;
				case "-u":
					ServiceManager.DeleteService(GetServiceName());
					return true;
				case "-?":
				case "-h":
					Console.Out.WriteLine(@"
MG Service controller:
-i install		service with the same name as exe fileName
-u uninstall	service with the same name as exe fileName
console			run as console application
");
					return true;


			}
			return false;

		}

		/// <summary>
		/// Sets the description of the service
		/// </summary>
		/// <param name="ServiceName"></param>
		/// <param name="Description"></param>
		public static void SetServiceDescription(string ServiceName, string Description)
		{
			Microsoft.Win32.RegistryKey system, currentControlSet,services, service;

			//Open the HKEY_LOCAL_MACHINE\SYSTEM key
			system = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("System");
			//Open CurrentControlSet
			currentControlSet = system.OpenSubKey("CurrentControlSet");
			//Go to the services key
			services = currentControlSet.OpenSubKey("Services");
			//Open the key for your service, and allow writing
			service = services.OpenSubKey(ServiceName, true);
			//Add your service's description as a REG_SZ value named "Description"
			service.SetValue("Description", Description);
			// Close everything uesd...
			service.Close();
			services.Close();
			currentControlSet.Close();
			system.Close();
		}

		/// <summary>
		/// Creates a service
		/// </summary>
		/// <param name="ServiceName"></param>
		/// <param name="DisplayName"></param>
		/// <param name="StartMode">Auto, Manual or something else</param>
		/// <param name="BinaryPathName">a path to image of service</param>
		/// <param name="LoadOrderGroup">group of service</param>
		/// <param name="ServicesDependedOn">what services it depends on</param>
		public static void CreateService(string ServiceName, string DisplayName, ServiceStartMode StartMode ,string BinaryPathName, string LoadOrderGroup, string[] ServicesDependedOn)
		{
			string dependString = "\0\0";
			if ((ServicesDependedOn!=null)&&(ServicesDependedOn.Length > 0))
			{
				StringBuilder builder = new StringBuilder();
				for (int i = 0; i < ServicesDependedOn.Length; i++)
				{
					string s = ServicesDependedOn[i];
					try
					{
						ServiceController controller1 = new ServiceController(s, ".");
						s = controller1.ServiceName;
					}
					catch
					{
					}
					builder.Append(s);
					builder.Append('\0');
				}
				builder.Append('\0');
				dependString = builder.ToString();
			}
		
			
			IntPtr SChandle = SafeNativeMethods.OpenSCManager(null, null, 0xf003f);
			IntPtr Shandle = IntPtr.Zero;
			if (SChandle == IntPtr.Zero)
			{
				throw new InvalidOperationException("Unable to open Service Manager!", new Win32Exception());
			}

			try
			{
				Shandle = NativeMethods.CreateService(SChandle, ServiceName, DisplayName, 0xf01ff, 0x10, (int) StartMode, 1, BinaryPathName, LoadOrderGroup, IntPtr.Zero, dependString, null, null);
				if (Shandle == IntPtr.Zero)
				{
					throw new Win32Exception();
				}
			}
			finally
			{
				if (Shandle != IntPtr.Zero)
				{
					SafeNativeMethods.CloseServiceHandle(Shandle);
				}
				SafeNativeMethods.CloseServiceHandle(SChandle);
			}

		}

		/// <summary>
		/// Removes Services in any case
		/// </summary>
		/// <param name="ServiceName"></param>
		public static void RemoveServiceIfExists(string ServiceName)
		{
			// at first we're trying to kill process because after deleting we'd not got enough info from registry
			try {KillServiceAsProcess(ServiceName);} catch {}
			try {DeleteService(ServiceName); } catch{}
		}

		/// <summary>
		/// Kills service process
		/// </summary>
		/// <param name="ServiceName"></param>
		public static void KillServiceAsProcess(string ServiceName)
		{
			try
			{
				string imageName;	
			{
				Microsoft.Win32.RegistryKey system, currentControlSet,services, service;

				system = Microsoft.Win32.Registry.LocalMachine.OpenSubKey("System");
				currentControlSet = system.OpenSubKey("CurrentControlSet");
				services = currentControlSet.OpenSubKey("Services");
				service = services.OpenSubKey(ServiceName, true);
				if (service!=null)
				{
					imageName = (string) service.GetValue("ImagePath");
					if (imageName!=null)  imageName = Path.GetFileNameWithoutExtension(imageName);
					else return;
					service.Close();
				} 
				else
				{
					services.Close();
					currentControlSet.Close();
					system.Close();
					return;
				}
				services.Close();
				currentControlSet.Close();
				system.Close();
			}
                
				// this function doesn't work as it expected after MSDN's readings :)
				Process[] procs = Process.GetProcessesByName(imageName);
				foreach (Process proc in procs)
				{
					try
					{
						proc.Kill();
						proc.Close();
					}
					catch
					{}
				}
			} catch
			{}
		}

		/// <summary>
		/// Stop service and then delete them
		/// </summary>
		/// <param name="ServiceName"></param>
		public static void DeleteService(string ServiceName)
		{
			try
			{
				using (ServiceController controller = new ServiceController(ServiceName))
				{
					if (controller.Status != ServiceControllerStatus.Stopped)
					{
						controller.Stop();
						controller.WaitForStatus(ServiceControllerStatus.Stopped, TimeSpan.FromMilliseconds(500));
						int tries = 10;
						controller.Refresh();
						while ((controller.Status != ServiceControllerStatus.Stopped) && (tries > 0))
						{
							controller.Refresh();
							tries--;
						}
					}
				}
			}
			catch
			{
				return;
			}
			IntPtr SChandler = SafeNativeMethods.OpenSCManager(null, null, 0xf003f);
			if (SChandler == IntPtr.Zero)
			{
				throw new Win32Exception();
			}
			IntPtr Shandler = IntPtr.Zero;
			try
			{
				Shandler = NativeMethods.OpenService(SChandler, ServiceName, 0x10000);
				if (Shandler == IntPtr.Zero)
				{
					throw new Win32Exception();
				}
				NativeMethods.DeleteService(Shandler);
			}
			finally
			{
				if (Shandler != IntPtr.Zero)
				{
					SafeNativeMethods.CloseServiceHandle(Shandler);
				}
				SafeNativeMethods.CloseServiceHandle(SChandler);
			}
		
		}
		
		/// <summary>
		/// Returns the status of given service
		/// </summary>
		/// <param name="serviceName"></param>
		/// <param name="machineName">if null, we use localhost</param>
		/// <returns></returns>
		public static ServiceControllerStatus GetServiceStatus(string serviceName, string machineName)
		{
			if (serviceName==null)
				throw new ArgumentNullException("serviceName");
			
			ServiceController sc;
			if (machineName!=null)
				sc = new ServiceController(serviceName, machineName);
			else 
				sc = new ServiceController(serviceName);
			
			try
			{
				return sc.Status;
			}
			finally
			{
				if (sc!=null)
					sc.Close();
			}
		}
		
		public static ServiceControllerStatus GetServiceStatus(string serviceName)
		{
			return GetServiceStatus(serviceName, null);
		}
		
	}
}
